package com.inns.position.redis.service.impl;

import com.inns.position.controller.ApiConsts;
import com.inns.position.exceptioin.InnsException;
import com.inns.position.exceptioin.InnsExceptionEnum;
import com.inns.position.hbase.StatType;
import com.inns.position.hbase.config.properties.InnsProperties;
import com.inns.position.kafka.bean.LocationInfo;
import com.inns.position.kafka.bean.LocationRealTimeInfo;
import com.inns.position.kafka.bean.TimePair;
import com.inns.position.redis.service.RealTimeDataService;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * Created by phoenix on 2018/4/21.
 */
@Service
public class RealTimeDataServiceImpl implements RealTimeDataService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    public static final String BUILD_ID_KEY = "buildIdSet";
    private Set<String> buildIdSet = new HashSet<>();

    @Override
    public Set<String> getBuildIds() {
        SetOperations<String, String> stringStringSetOperations = redisTemplate.opsForSet();
        return stringStringSetOperations.members(BUILD_ID_KEY);
    }

    /**
     * 实时数据存储
     *
     * @param locationInfo
     */
    @Override
    public void updateUserData(LocationInfo locationInfo) {
        setBuildId(locationInfo.getBuildId());
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        LocationRealTimeInfo info = opsForHash.get(locationInfo.getIMSI(), locationInfo.getBuildId());
        if (info != null) {
            info.update(locationInfo);
            opsForHash.put(locationInfo.getIMSI(), locationInfo.getBuildId(), info);
        } else {
            opsForHash.put(locationInfo.getIMSI(), locationInfo.getBuildId(), new LocationRealTimeInfo(locationInfo));
        }
    }

    private void setBuildId(String buildId) {
        if (buildIdSet.contains(buildId)) {
            return;
        }
        buildIdSet.add(buildId);

        SetOperations<String, String> stringStringSetOperations = redisTemplate.opsForSet();
        if (!stringStringSetOperations.isMember(BUILD_ID_KEY, buildId)) {
            stringStringSetOperations.add(BUILD_ID_KEY, buildId);
        }
    }

    @Override
    public void updateUserPoiData(LocationInfo locationInfo) {
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        String realId = getRealId(locationInfo.getIMSI(), locationInfo.getBuildId());
        LocationRealTimeInfo info = opsForHash.get(realId, locationInfo.getPoiId() + "");
        if (info != null) {
            info.update(locationInfo);
            opsForHash.put(realId, locationInfo.getPoiId() + "", info);
        } else {
            opsForHash.put(realId, locationInfo.getPoiId() + "", new LocationRealTimeInfo(locationInfo));
        }
    }

    private String getRealId(String imsi, String buildId) {
        return imsi + "|" + buildId;
    }

    @Override
    public void updateBuildData(LocationInfo locationInfo) {
        HashOperations<String, String, Long> opsForHash = redisTemplate.opsForHash();
        opsForHash.put(locationInfo.getBuildId(), locationInfo.getIMSI(), locationInfo.getTimestamp());
//        opsForHash.putIfAbsent(locationInfo.getBuildId(), locationInfo.getIMSI(), locationInfo.getTimestamp());
    }

    @Override
    public Map<String, Long> getBuildImsiListByTime(String buildId, long startTime) {
        HashOperations<String, String, Long> opsForHash = redisTemplate.opsForHash();
        Map<String, Long> map = new HashMap<>();
        Map<String, Long> entries = opsForHash.entries(buildId);
        for (Map.Entry<String, Long> m : entries.entrySet()) {
            if (m.getValue() >= startTime) {
                map.put(m.getKey(), m.getValue());
            }
        }
        return map;
    }

    @Override
    public boolean isNewUser(String imsi, String buildId, long splitTime) {
        LocationRealTimeInfo info = getRealTimeInfoByIMSI(buildId, imsi);
        return isNewUser(splitTime, info);
    }

    private boolean isNewUser(long splitTime, LocationRealTimeInfo info) {
        if (info != null) {
            List<TimePair> times = info.getAllAccessTimes();
            int size = times.size();
            if (size == 1) {
                return true;
            }
            TimePair timePair = times.get(0);// 最久的一次访问
            return timePair.getStartTime() > splitTime;// 如果第一次访问也在 检查周期之内
        }
        return false;
    }

    @Override
    public List<Map<String, String>> getAllPositionInfoByBuildId(String buildId) {
        HashOperations<String, String, Long> opsForHash = redisTemplate.opsForHash();
        Map<String, Long> entries = opsForHash.entries(buildId);

        long curTime = System.currentTimeMillis();
        List<Map<String, String>> list = new ArrayList<>();
        for (Map.Entry<String, Long> e : entries.entrySet()) {
            if (curTime - e.getValue() > InnsProperties.instance().getRealTimeOnlineTime() * 1000) {
                continue;
            }
            Map<String, String> map = new HashMap<>();
            map.put(ApiConsts.IMSI, e.getKey());

            LocationRealTimeInfo info = (LocationRealTimeInfo) redisTemplate.opsForHash().get(e.getKey(), buildId);

            map.put("x", info.getX() + "");
            map.put("y", info.getY() + "");
            list.add(map);
        }
        return list;
    }

    @Override
    public Map<String, String> getPositionInfoByIMSI(String buildId, String imsi) {
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        LocationRealTimeInfo locationRealTimeInfo = opsForHash.get(imsi, buildId);
        if (locationRealTimeInfo == null) {
            throw new InnsException(InnsExceptionEnum.USER_NOT_FIND);
        }

        Map<String, String> map = new HashMap<>();
        map.put(ApiConsts.IMSI, imsi);
        map.put(ApiConsts.BUILD_ID, buildId);
        map.put(ApiConsts.FLOOR_ID, locationRealTimeInfo.getFloorId());
        map.put("x", locationRealTimeInfo.getX() + "");
        map.put("y", locationRealTimeInfo.getY() + "");
        List<TimePair> accessTimes = locationRealTimeInfo.getAllAccessTimes();
        map.put(ApiConsts.ACCESS_FRE_CNT, accessTimes.size() + "");
        map.put(ApiConsts.FIRST_DATE, accessTimes.get(0).getStartTime() + "");
        return map;
    }

    @Override
    public LocationRealTimeInfo getRealTimeInfoByIMSI(String buildId, String imsi) {
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(imsi, buildId);
    }

    @Override
    public LocationRealTimeInfo getRealTimeInfoByPoi(String buildId, String imsi, Integer poi) {
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(getRealId(imsi, buildId), poi);
    }

    @Override
    public Map<String, LocationRealTimeInfo> getRealTimeInfoPoiByIMSI(String buildId, String imsi) {
        HashOperations<String, String, LocationRealTimeInfo> opsForHash = redisTemplate.opsForHash();
        return opsForHash.entries(getRealId(imsi, buildId));
    }

    @Override
    public Map<String, String> getStatisticsInfoByBuildId(String buildId) {
        HashOperations<String, String, Long> opsForHash = redisTemplate.opsForHash();
        Map<String, Long> entries = opsForHash.entries(buildId);
        if (entries == null) {
            throw new InnsException(InnsExceptionEnum.BUILD_ID_NOT_FIND);
        }

        int realTimeCount = 0;
        int dayCount = 0;
        int newCount = 0;
        long sumTime = 0;

        long curTime = System.currentTimeMillis();
        long rt = curTime - InnsProperties.instance().getRealTimeOnlineTime() * 1000;
        long dt = StatType.DAY.getStartTime(curTime);
        for (Map.Entry<String, Long> e : entries.entrySet()) {
            long accTime = e.getValue();
            if (accTime > rt) {
                realTimeCount++;//实时用户数
            }
            if (accTime > dt) {
                dayCount++;
                LocationRealTimeInfo info = getRealTimeInfoByIMSI(buildId, e.getKey());
                if (isNewUser(dt, info)) {
                    newCount++; //今天加进来的用户数
                }
                List<TimePair> timePairList = info.getAccessTimesByStartTime(dt);
                for (TimePair timePair : timePairList) {
                    sumTime += timePair.getDurTime();
                }
            }
        }

        Map<String, String> map = new HashMap<>();
        map.put(ApiConsts.BUILD_ID, buildId);
        map.put(ApiConsts.RT_USR_CNT, realTimeCount + "");// 实时用户数
        map.put(ApiConsts.ACC_USR_CNT, dayCount + "");// 累计用户数
        map.put(ApiConsts.NEW_USR_CNT, newCount + "");// 新增用户数
        String avgResideDur = "0";
        if (sumTime != 0l) {
            avgResideDur = sumTime / dayCount + "";
        }
        map.put(ApiConsts.AVG_RESIDE_DUR, avgResideDur);// 平均驻留时长
        return map;
    }

}
